package com.cskaoyan.wordmemorize.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.cskaoyan.wordmemorize.common.exception.BusinessException;
import com.cskaoyan.wordmemorize.converter.UserConverter;
import com.cskaoyan.wordmemorize.dao.entity.*;
import com.cskaoyan.wordmemorize.dao.mapper.*;
import com.cskaoyan.wordmemorize.dto.admin.LevelPrivilegeDTO;
import com.cskaoyan.wordmemorize.dto.admin.PageDTO;
import com.cskaoyan.wordmemorize.dto.app.*;
import com.cskaoyan.wordmemorize.request.PageRequest;
import com.cskaoyan.wordmemorize.request.UserRemindCommand;
import com.cskaoyan.wordmemorize.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

@Service
public class UserServiceImpl implements UserService {
    @Resource
    UserMapper userMapper;

    @Resource
    UserConverter userConverter;

    @Resource
    UserCheckinStatisticsMapper userCheckinStatisticsMapper;
    @Resource
    UserCheckinMapper userCheckinMapper;
    @Resource
    LevelMapper levelMapper;
    @Resource
    LevelPrivilegeMapper levelPrivilegeMapper;
    @Resource
    GoodsMapper goodsMapper;
    @Resource
    UserRemindMapper userRemindMapper;
    @Resource
    UserVocBoundStatisticsMapper userVocBoundStatisticsMapper;


    // APP端获取用户信息
    @Override
    public UserDTO getUserInfo(Long userId) {
//        UserDO userDO = userMapper.getUserInfo(userId);
//
//        return userConverter.userDO2DTO(userDO);


        // 查询基本信息
        UserDO userDO = userMapper.getUserInfo(userId);
        // 查询等级对应的特权码

        LambdaQueryWrapper<UserVocBoundStatisticsDO> userVocBoundStatisticWrapper = new LambdaQueryWrapper<>();
        userVocBoundStatisticWrapper
                .eq(UserVocBoundStatisticsDO::getUserId, userId)
                .select(UserVocBoundStatisticsDO::getAvailable);
        return userConverter.userDO2DTO(userDO);
    }

    //    ------------------------------- 叶佳杰部分： ---------------------------------
    // APP端获取用户带升级信息
    @Override
    public NextLevelDTO getUserNextDTO(Long userId) {
/*        先获取当前用户的等级值
          在根据他昨天是否签到以及用户是否从昨天到今天升级来判断用户是否购买等级（注意，每升一级都要判断是否是通过购买等级实现的升级）
          分为两种情况：1.没有购买等级，2.购买等级
      1.没有购买等级：
          用户没有购买等级分为两种情况：
                1.1）6级前是累计签到：
                    计算过程：
                        a.查询当前用户等级所对应的签到天数
                        b.再查询用户下一级所对应的需要签到天数
                        c.用下一级所对应的需要签到天数-用户当前等级所对应的签到天数
                1.2）5级前到6级后（从累计签到到连续签到）
                    计算过程：
                        a.和6级后是连续签到逻辑一样
                1.3）6级后是连续签到（麻烦）：
                    计算过程：
                        a.用户当前最大连续签到天数：从user_checkin_statistics表中查询cur_continuous_days获得用户当前最大连续签到天数
                        b.查询用户下一级所对应的需要签到天数
                        c.用下一级所需要的签到天数-用户当前最大连续签到天数
      2.购买等级：
          用户购买等级分为两种情况：
                2.1）6级前是累计签到：
                    计算过程：
                        a.查询购买等级后达到的等级所对应的签到天数（注意：这时候数据库里实际用户的签到天数还是他没通过购买等级时所对应的签到天数，不变）
                        b.再查询用户下一级所对应的需要签到天数
                        c.用下一级所对应的需要签到天数-用户购买等级后达到的等级所对应的签到天数
                2.2）5级前到6级后（从累计签到到连续签到）
                        a.和6级后连续签到逻辑一样
                2.3）6级后是连续签到（麻烦）：
                    计算过程：
                        a.查询用户购买等级后当前最大连续签到天数：从user_checkin_statistics表中查询cur_continuous_days获得用户当前最大连续签到天数（注意：用户实际连续签到天数还是没他没通过购买升级时所对应的签到天数）
                        b.查询用户下一级所对应的需要签到天数
                        c.用下一级所需要的签到天数-用户当前最大连续签到天数

 */
//        从user表里检查用户是否在数据库中存在
        UserDO user = userMapper.getAllUserInfo(userId);
        if (user == null) {
            throw new BusinessException("角色不存在或已被删除", null);
        }
//        从user表里获取用户当前的等级值
        Integer currentLevel = user.getLevelValue();
//        从user表里获取用户最近一次升级的日期
        LocalDate currentLevelDate = user.getLevelDate();
//        通过获取今天的日期，和昨天的日期后续用来判断一下用户是否是通过购买升级的
        LocalDate today = LocalDate.now();
        LocalDate yesterday = today.minusDays(1);
//        通过userCheckinStatistics表查询该用户是否有签到的记录
        UserCheckinStatisticsDO userCheckinStatisticsDO = userCheckinStatisticsMapper.selectByUserId(userId);
        if (userCheckinStatisticsDO == null) {
            throw new BusinessException("这个用户没有签到记录", null);
        }
//        在user_checkin表里查询用户昨天是否有签到(包括正常或者是补签)，后续用来判断是否通过购买来升的级
        boolean isYesterdaySigned = userCheckinMapper.existsYesterdaySign(userId, yesterday);
//        在level表里查询用户当前等级所对应信息
        LevelDO currentLevelConfig = levelMapper.selectByLevelValue(currentLevel);
//        在level表里查询用户下一级等级所对应信息
        LevelDO nextLevelConfig = levelMapper.selectByLevelValue(currentLevel + 1);
//        排除用户已经到达了最顶级12级
        if (nextLevelConfig == null) {
            return null;
        }
//        创建一个变量用来存放用户是否通过购买生的等级,false就是没有买
        boolean isPurchased = false;
//        创建对象，用来返回距离下一级还需要多少天升级
        NextLevelDTO dto = new NextLevelDTO();
//        判断用户是否在昨天升级，分为两种情况，用户在昨天升级了，用户在昨天没有升级、
//        1.用户在昨天升级了
        if (currentLevelDate != null && currentLevelDate.equals(yesterday)) {
//            如果用户在昨天试了，就检查用户在昨天有没有签到记录，没有就说明他是买上去的，
            if (!isYesterdaySigned) {
                isPurchased = true;
//                在这里加了下饭龙的需求：如果用户昨天没有打卡记录，让最大连续签到天数归0
                UpdateWrapper<UserCheckinStatisticsDO> updateWrapper = new UpdateWrapper<>();
                updateWrapper.eq("user_id", userId)
                        .eq("is_deleted", 0)
                        .set("cur_continuous_days", 0);
                int update = userCheckinStatisticsMapper.update(updateWrapper);
                System.out.println("用户昨天没打卡，今天让他的最大连续签到天数归0 = " + update);
            }
        }
//        2.用户在昨天没有升级
        if (!isPurchased) {
//            通过上面查询到的 在level表里查询用户当前等级所对应信息currentLevelConfig，拿到用户当前等级所需要签到的天数
            Integer requiredDays = currentLevelConfig.getCheckInDays();
//            通过用户目前的等级所对应的签到类型去拿去用户实际的签到天数
            int actualDays;
//            如果用户是连续签到就拿取当前最大连续签到
//            如果用户是累计签到就拿取总的签到天数
            if (currentLevelConfig.getCheckInType() == 1) {
                actualDays = userCheckinStatisticsDO.getCurContinuousDays();
            } else {
                actualDays = userCheckinStatisticsDO.getTotalDays();
            }
//            实际的签到天数如果小于用户当前等级的签到天数，那么这小子就是氪佬
            isPurchased = actualDays < requiredDays;
        }
//        接下来计算用户升级剩余天数
//        1.如果用户不是通过购买升级的
        if (!isPurchased) {
//            获取用户的当前等级值，6记前，累计签到
            if (currentLevel < 5) {
//                设置为已累计签到
                dto.setSignType(2);
//                计算还有多少天升级，用用户下一级的天数-总签到天数
                dto.setRemainDays(nextLevelConfig.getCheckInDays() - userCheckinStatisticsDO.getTotalDays());
            } else {
//            获取用户的当前等级值，6记后，连续签到
                dto.setSignType(1);
//                计算还有多少天升级，用用户下一级还需要连续签到的天数-当前等级最大连续签到天数
                dto.setRemainDays(nextLevelConfig.getCheckInDays() - userCheckinStatisticsDO.getCurContinuousDays());
            }

        } else {
//            2.如果用户是通过购买升级的
//            获取用户的当前等级值，6记前，累计签到
            if (currentLevel < 5) {
//                设置为已累计签到
                dto.setSignType(2);
//                计算还有多少天升级，用用户下一级升级所用的的天数-当前等级升级所用的的天数
                dto.setRemainDays(nextLevelConfig.getCheckInDays() - currentLevelConfig.getCheckInDays());
            } else {
//            获取用户的当前等级值，6记后，连续签到
                dto.setSignType(1);
//                计算还有多少天升级，用用户下一级还需要连续签到的天数-当前等级最大连续签到天数
                dto.setRemainDays(nextLevelConfig.getCheckInDays() - userCheckinStatisticsDO.getCurContinuousDays());

            }
        }
//        要让剩余的天数不能为负数
        dto.setRemainDays(Math.max(dto.getRemainDays(), 0));
        return dto;
    }

    // APP端获取用户等级信息(包括已达到等级和未达到等级)
    @Override
    public DisplayUserLevelDTO getUserLevelInfo(Long userId) {
//        查询用户基本信息是否存在
        UserDO userDO = userMapper.selectById(userId);
        if (userDO == null || userDO.getIsDeleted() == 1) {
            throw new BusinessException("用户不存在", null);
        }
//        从user表里查询出来的当前user对象获取用户的当前等级值
        Integer currentLevelValue = userDO.getLevelValue();
//        如果这个人当前等级值没有，那就给他一个默认值，防止影响后面的查询
        if (currentLevelValue == null) {
            currentLevelValue = 0;
        }
//        接下来去level表里查询出来所有的有效的所有等级对应的信息-多少级升级等等
        QueryWrapper<LevelDO> levelDOQueryWrapper = new QueryWrapper<>();
        levelDOQueryWrapper.eq("is_deleted", 0)
                .orderByAsc("level_value");
        List<LevelDO> allLevels = levelMapper.selectList(levelDOQueryWrapper);
//        创建返回给前端的结果对象
        DisplayUserLevelDTO result = new DisplayUserLevelDTO();
//        返回已达到等级的列表（用户当前等级以下）
        List<LevelPageDTO> nowLevelList = new ArrayList<>();
//        返回未达到等级列表（用户当前等级以上）
        List<LevelPageDTO> upperLevelList = new ArrayList<>();
//        遍历所以等级信息
        for (LevelDO level : allLevels) {
//            将level转化为我们需要的levelpagedto
            LevelPageDTO levelPageDTO = userConverter.levelDO2LevelPageDTO(level);
//            获取当前等级所有的有效权限
            QueryWrapper<LevelPrivilegeDO> levelPrivilegeDOQueryWrapper = new QueryWrapper<>();
            levelPrivilegeDOQueryWrapper.eq("level_id", level.getId())
                    .eq("is_deleted", 0);
            List<LevelPrivilegeDO> privileges = levelPrivilegeMapper.selectList(levelPrivilegeDOQueryWrapper);
//            将所有的查出来对应的LevelPrivilegeDO转化为他的dto，之后在给levelPageDTO的levelprivileges赋值
//            创建新列表存储转换后的DTO
            List<LevelPrivilegeDTO> privilegeDTOList = new ArrayList<>();
//            显式循环遍历每个特权对象
            for (LevelPrivilegeDO privilege : privileges) {
//            调用转换方法，逐个转换
                LevelPrivilegeDTO dto = userConverter.levelPrivilegeDO2DTO(privilege);
//            将转换后的DTO添加到列表
                privilegeDTOList.add(dto);
            }
//            设置转换后的特权列表到DTO
            levelPageDTO.setLevelPrivileges(privilegeDTOList);
            // 4.3 获取当前等级对应的解锁商品（用于升级的商品）
            GoodsDO unlockGoods = goodsMapper.selectOne(
                    new QueryWrapper<GoodsDO>()
                            // 商品类型限制：2表示等级解锁类型商品
                            .eq("type", 2)
                            // 商品值匹配等级值（商品value字段存储目标等级值）
                            .eq("value", level.getLevelValue())
                            // 过滤条件：未删除的商品
                            .eq("is_deleted", 0)
                            // 过滤条件：商品处于上架状态（可购买）
                            .eq("status", 1)
            );
            // 若存在有效商品，则设置商品ID到DTO
            if (unlockGoods != null) {
                levelPageDTO.setGoodsId(unlockGoods.getId().toString());
            }

            // 4.4 根据用户当前等级进行分类
            if (level.getLevelValue() <= currentLevelValue) {
                // 已达到的等级（当前等级及以下）加入已达到列表
                nowLevelList.add(levelPageDTO);
            } else {
                // 未达到的等级（当前等级以上）加入未达到列表
                upperLevelList.add(levelPageDTO);
            }
        }
        // 5. 对结果列表进行二次排序（确保前端展示顺序）
        // 已达到等级按等级值升序排列（从低到高）
        nowLevelList.sort(Comparator.comparingInt(LevelPageDTO::getLevelValue));
        // 未达到等级按等级值升序排列（从低到高）
        upperLevelList.sort(Comparator.comparingInt(LevelPageDTO::getLevelValue));

        // 设置结果集到返回对象
        result.setNowLevel(nowLevelList);
        result.setUpperLevel(upperLevelList);
        return result;
    }

    // APP端更新用户每天计划学习的单词量
    @Override
    public void updateVocCountOfDay(Long userId, Integer count) {
//        在controller层保证每天最少20个
        UpdateWrapper<UserDO> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", userId)
                .eq("is_deleted", 0)
                .set("voc_count_of_day", count);
        int updateNum = userMapper.update(updateWrapper);
        System.out.println("updateNum = " + updateNum);
    }

    // APP端更新用户的学习提醒时间
    @Override
    public void updateUserRemind(UserRemindCommand command) {
        UserRemindDO userRemindDO = userConverter.userRemindCommand2DO(command);
        LambdaUpdateWrapper<UserRemindDO> userRemindWrapper = new LambdaUpdateWrapper<>();
        userRemindWrapper.eq(UserRemindDO::getUserId, userRemindDO.getUserId())
                .set(UserRemindDO::getRemindTime, userRemindDO.getRemindTime());
        userRemindMapper.update(null, userRemindWrapper);
    }

    // APP端更获取用户的提醒信息-没有
    @Override
    public UserRemindDTO getUserRemind(Long userId) {
        return null;
    }

    // APP端发送消息提醒(在定时任务中调用该方法将待发送的提醒信息放入延迟队列)-没有
    @Override
    public void messageRemind() {


    }

    // APP端发获取用户的单词上限变化明细，如果未传分页信息则返回所有-没有
    @Override
    public PageDTO<DisplayUserVocBoundLogDTO> getUserVocBoundLog(PageRequest pageRequest, Long userId) {
        return null;
    }

    // APP端获取用户的单词上限统计信息
    @Override
    public UserVocBoundStatisticsDTO getUserVocBoundStatistics(Long userId) {
//        QueryWrapper<UserVocBoundStatisticsDO> queryWrapper =new QueryWrapper<>();
//        queryWrapper.eq("user_id",userId)
//                .eq("is_deleted",0);
//        UserVocBoundStatisticsDO userVocBoundStatisticsDO = userVocBoundStatisticsMapper.selectOne(queryWrapper);
//        UserVocBoundStatisticsDTO userVocBoundStatisticsDTO = userConverter.userVocBoundStatisticsDO2DisplayDTO(userVocBoundStatisticsDO);
//        return userVocBoundStatisticsDTO;
        LambdaQueryWrapper<UserVocBoundStatisticsDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserVocBoundStatisticsDO::getUserId, userId);
        UserVocBoundStatisticsDO userVocBoundLogStaticsDO = userVocBoundStatisticsMapper.selectOne(queryWrapper);

        return userConverter.userVocBoundStatisticsDO2DisplayDTO(userVocBoundLogStaticsDO);
    }

    // APP端获取用户我的页面展示的信息
    @Override
    public UserPageDTO getUserPage(Long userId) {
        UserPageDTO userPageDTO = new UserPageDTO();
//        在user表：查询每天规划单词学习量、是否哦有补签卡
        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", userId)
                .eq("is_deleted", 0);
        UserDO userDO = userMapper.selectOne(queryWrapper);
//        在user_voc_bound_statistics表里：查询总的单词上限数量、免费获取的单词上限数量、购买的单词上限数量
        QueryWrapper<UserVocBoundStatisticsDO> queryWrapperUserVocBoundStatisticsDO = new QueryWrapper<>();
        queryWrapperUserVocBoundStatisticsDO.eq("user_id", userId)
                .eq("is_deleted", 0);
        UserVocBoundStatisticsDO userVocBoundStatisticsDO = userVocBoundStatisticsMapper.selectOne(queryWrapperUserVocBoundStatisticsDO);
//        在user_remind表里：查询每日提醒学习时间, 格式为 HH:mm 24小时制
        QueryWrapper<UserRemindDO> queryWrapperUserRemindDO = new QueryWrapper<>();
        queryWrapperUserRemindDO.eq("user_id", userId)
                .eq("is_deleted", 0);
        UserRemindDO userRemindDO = userRemindMapper.selectOne(queryWrapperUserRemindDO);
//        赋值
        userPageDTO.setVocCountOfDay(userDO.getVocCountOfDay());
        userPageDTO.setReSignCard(userDO.getHasSignCard());
        userPageDTO.setTotalVocBound(userVocBoundStatisticsDO.getTotal());
        userPageDTO.setFreeVocBound(userVocBoundStatisticsDO.getFree());
        userPageDTO.setPayVocBound(userVocBoundStatisticsDO.getPay());
        userPageDTO.setRemindTime(userRemindDO.getRemindTime().format(DateTimeFormatter.ofPattern("HH:mm")));
        return userPageDTO;
    }
}
